﻿/*
# -*- coding: utf-8 -*-
# 注意：

# 若希望将本代码用于商业用途，需先联系作者并获得明确授权。
# 作者不对因使用本代码引发的任何直接或间接损失负责。
# 如有违反上述条款的行为，作者保留追究法律责任的权利。

# 免责声明：
# 请尊重知识产权，遵循相关法律法规。

# Notice:
# If you wish to use this code for commercial purposes, you must contact the author and obtain explicit authorization.
# The author is not responsible for any direct or indirect losses caused by the use of this code.
# If any of the above terms are violated, the author reserves the right to pursue legal action.
# Disclaimer:
# Please respect intellectual property rights and comply with relevant laws and regulations.

# Внимание:
# Если вы хотите использовать этот код в коммерческих целях, вам необходимо связаться с автором и получить явное разрешение.
# Автор не несет ответственности за любые прямые или косвенные убытки, возникшие в результате использования этого кода.
# В случае нарушения вышеуказанных условий автор оставляет за собой право на юридическое преследование.
# Отказ от ответственности:
# Пожалуйста, уважайте интеллектуальную собственность и соблюдайте соответствующие законы и нормативные акты.

# Attention :
# Si vous souhaitez utiliser ce code à des fins commerciales, vous devez contacter l'auteur et obtenir une autorisation explicite.
# L'auteur n'est pas responsable des pertes directes ou indirectes causées par l'utilisation de ce code.
# En cas de violation des conditions ci-dessus, l'auteur se réserve le droit de poursuivre une action en justice.
# Avertissement :
# Veuillez respecter les droits de propriété intellectuelle et vous conformer aux lois et réglementations en vigueur.

# 注意：

# 若希望将本代码用于商业用途，需先联系作者并获得明确授权。
# 作者不对因使用本代码引发的任何直接或间接损失负责。
# 如有违反上述条款的行为，作者保留追究法律责任的权利。

# 免责声明：
# 请尊重知识产权，遵循相关法律法规。

# Notice:
# If you wish to use this code for commercial purposes, you must contact the author and obtain explicit authorization.
# The author is not responsible for any direct or indirect losses caused by the use of this code.
# If any of the above terms are violated, the author reserves the right to pursue legal action.
# Disclaimer:
# Please respect intellectual property rights and comply with relevant laws and regulations.

# Внимание:
# Если вы хотите использовать этот код в коммерческих целях, вам необходимо связаться с автором и получить явное разрешение.
# Автор не несет ответственности за любые прямые или косвенные убытки, возникшие в результате использования этого кода.
# В случае нарушения вышеуказанных условий автор оставляет за собой право на юридическое преследование.
# Отказ от ответственности:
# Пожалуйста, уважайте интеллектуальную собственность и соблюдайте соответствующие законы и нормативные акты.

# Attention :
# Si vous souhaitez utiliser ce code à des fins commerciales, vous devez contacter l'auteur et obtenir une autorisation explicite.
# L'auteur n'est pas responsable des pertes directes ou indirectes causées par l'utilisation de ce code.
# En cas de violation des conditions ci-dessus, l'auteur se réserve le droit de poursuivre une action en justice.
# Avertissement :
# Veuillez respecter les droits de propriété intellectuelle et vous conformer aux lois et réglementations en vigueur.

# 注意：

# 若希望将本代码用于商业用途，需先联系作者并获得明确授权。
# 作者不对因使用本代码引发的任何直接或间接损失负责。
# 如有违反上述条款的行为，作者保留追究法律责任的权利。

# 免责声明：
# 请尊重知识产权，遵循相关法律法规。

# Notice:
# If you wish to use this code for commercial purposes, you must contact the author and obtain explicit authorization.
# The author is not responsible for any direct or indirect losses caused by the use of this code.
# If any of the above terms are violated, the author reserves the right to pursue legal action.
# Disclaimer:
# Please respect intellectual property rights and comply with relevant laws and regulations.

# Внимание:
# Если вы хотите использовать этот код в коммерческих целях, вам необходимо связаться с автором и получить явное разрешение.
# Автор не несет ответственности за любые прямые или косвенные убытки, возникшие в результате использования этого кода.
# В случае нарушения вышеуказанных условий автор оставляет за собой право на юридическое преследование.
# Отказ от ответственности:
# Пожалуйста, уважайте интеллектуальную собственность и соблюдайте соответствующие законы и нормативные акты.

# Attention :
# Si vous souhaitez utiliser ce code à des fins commerciales, vous devez contacter l'auteur et obtenir une autorisation explicite.
# L'auteur n'est pas responsable des pertes directes ou indirectes causées par l'utilisation de ce code.
# En cas de violation des conditions ci-dessus, l'auteur se réserve le droit de poursuivre une action en justice.
# Avertissement :
# Veuillez respecter les droits de propriété intellectuelle et vous conformer aux lois et réglementations en vigueur.

# 注意：

# 若希望将本代码用于商业用途，需先联系作者并获得明确授权。
# 作者不对因使用本代码引发的任何直接或间接损失负责。
# 如有违反上述条款的行为，作者保留追究法律责任的权利。

# 免责声明：
# 请尊重知识产权，遵循相关法律法规。

# Notice:
# If you wish to use this code for commercial purposes, you must contact the author and obtain explicit authorization.
# The author is not responsible for any direct or indirect losses caused by the use of this code.
# If any of the above terms are violated, the author reserves the right to pursue legal action.
# Disclaimer:
# Please respect intellectual property rights and comply with relevant laws and regulations.

# Внимание:
# Если вы хотите использовать этот код в коммерческих целях, вам необходимо связаться с автором и получить явное разрешение.
# Автор не несет ответственности за любые прямые или косвенные убытки, возникшие в результате использования этого кода.
# В случае нарушения вышеуказанных условий автор оставляет за собой право на юридическое преследование.
# Отказ от ответственности:
# Пожалуйста, уважайте интеллектуальную собственность и соблюдайте соответствующие законы и нормативные акты.

# Attention :
# Si vous souhaitez utiliser ce code à des fins commerciales, vous devez contacter l'auteur et obtenir une autorisation explicite.
# L'auteur n'est pas responsable des pertes directes ou indirectes causées par l'utilisation de ce code.
# En cas de violation des conditions ci-dessus, l'auteur se réserve le droit de poursuivre une action en justice.
# Avertissement :
# Veuillez respecter les droits de propriété intellectuelle et vous conformer aux lois et réglementations en vigueur.

# 注意：

# 若希望将本代码用于商业用途，需先联系作者并获得明确授权。
# 作者不对因使用本代码引发的任何直接或间接损失负责。
# 如有违反上述条款的行为，作者保留追究法律责任的权利。

# 免责声明：
# 请尊重知识产权，遵循相关法律法规。

# Notice:
# If you wish to use this code for commercial purposes, you must contact the author and obtain explicit authorization.
# The author is not responsible for any direct or indirect losses caused by the use of this code.
# If any of the above terms are violated, the author reserves the right to pursue legal action.
# Disclaimer:
# Please respect intellectual property rights and comply with relevant laws and regulations.

# Внимание:
# Если вы хотите использовать этот код в коммерческих целях, вам необходимо связаться с автором и получить явное разрешение.
# Автор не несет ответственности за любые прямые или косвенные убытки, возникшие в результате использования этого кода.
# В случае нарушения вышеуказанных условий автор оставляет за собой право на юридическое преследование.
# Отказ от ответственности:
# Пожалуйста, уважайте интеллектуальную собственность и соблюдайте соответствующие законы и нормативные акты.

# Attention :
# Si vous souhaitez utiliser ce code à des fins commerciales, vous devez contacter l'auteur et obtenir une autorisation explicite.
# L'auteur n'est pas responsable des pertes directes ou indirectes causées par l'utilisation de ce code.
# En cas de violation des conditions ci-dessus, l'auteur se réserve le droit de poursuivre une action en justice.
# Avertissement :
# Veuillez respecter les droits de propriété intellectuelle et vous conformer aux lois et réglementations en vigueur.

 */

using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;

namespace FMGames {

    public class CubeObject : MonoBehaviour {
        [SerializeField] private Rigidbody rb;
        [SerializeField] private MeshRenderer rendererR;
        [SerializeField] private Collider collider;

        private CubeData cubeData;
        public CubeData CubeData { get => cubeData; private set => cubeData = value; }

        [SerializeField] private TMP_Text tmpText;

        [Space(12)]
        [Header("Audio")]
        [SerializeField] AudioSource audioSource;
        [SerializeField] AudioClip[] hitSound, mergeSound;

        private bool isBouncing = true;

        private float mergeTimer;


        private void Update() {
            transform.localEulerAngles = new Vector3(0, 0, transform.localEulerAngles.z);
        }

        private void FixedUpdate() {
            mergeTimer += Time.fixedDeltaTime;
        }

        public void Init(CubeData data) {
            rb.useGravity = false;
            rb.isKinematic = true;

            collider.enabled = false;

            CubeData = data;
            SetDesign();

            transform.localScale = new Vector3(CubeData.Scale, CubeData.Scale, 0.7f);
        }

        private void OnCollisionEnter(Collision col) {
            PlayPopEffect();

            CubeObject otherCube = col.gameObject.GetComponent<CubeObject>();
            if (otherCube != null) {
                DisableBouncing();
                TryMerge(otherCube);
            }

            if (col.gameObject.CompareTag("BottomBorder")) {
                DisableBouncing();
            }
        }

        private void OnTriggerEnter(Collider other) {
            if (other.CompareTag("Death")) {
                if (!isBouncing) {
                    GameManager._instance.GameOver(false);
                }
            }
        }

        private void OnTriggerStay(Collider other) {
            if (other.CompareTag("Death")) {
                if (!isBouncing) {
                    GameManager._instance.GameOver(false);
                }
            }
        }

        private void OnCollisionStay(Collision col) {
            CubeObject otherCube = col.gameObject.GetComponent<CubeObject>();
            if (otherCube != null) {
                TryMerge(otherCube);
            }
        }

        [ContextMenu("Shoot")]
        private void ShootFromInspector() {
            Shoot(7);
        }

        public void Shoot(float speed) {
            transform.SetParent(null);

            collider.enabled = true;
            rb.isKinematic = false;

            rb.velocity = transform.up * speed;
        }

        /// <summary>
        /// Checks if merging is possible with the collided other cube
        /// </summary>
        /// <param name="otherCube"></param>
        private void TryMerge(CubeObject otherCube) {
            if (otherCube.CubeData.Id != CubeData.Id) return;
            if (mergeTimer < 0.5f || otherCube.mergeTimer < 0.5f) return;

            CubeObject toDestroy, toUpgrade;
            if (otherCube.transform.position.y > transform.position.y) {
                toDestroy = this;
                toUpgrade = otherCube;
            } else {
                toDestroy = otherCube;
                toUpgrade = this;
            }

            GameManager._instance.GetLevel().MergeCubes(toUpgrade, toDestroy, DataCollection._instance.CubeDatas[CubeData.Id + 1].Value);

            Destroy(toDestroy.gameObject);

            toUpgrade.MergeUpgrade(1);
        }

        /// <summary>
        /// Upgrades the cube
        /// </summary>
        /// <param name="levelsToAdd"></param>
        public void MergeUpgrade(int levelsToAdd) {
            mergeTimer = 0;

            CubeData = DataCollection._instance.CubeDatas[CubeData.Id + levelsToAdd];
            SetDesign();

            StopAllCoroutines();
            StartCoroutine(MergeCoroutine());

            PlayMergeEffect();
        }

        private void DisableBouncing() {
            collider.material.bounciness = 0;

            collider.material.dynamicFriction = 0.5f;
            collider.material.staticFriction = 0.5f;
            collider.material.frictionCombine = PhysicMaterialCombine.Maximum;

            rb.useGravity = true;

            isBouncing = false;
        }

        /// <summary>
        /// Changes the scale of the cube with lerping
        /// </summary>
        /// <returns></returns>
        private IEnumerator MergeCoroutine() {
            Vector3 baseScale = transform.localScale;
            Vector3 endScale = new Vector3(baseScale.x * 0.85f, baseScale.x * 0.85f, baseScale.z);
            Vector3 endScale2 = new Vector3(CubeData.Scale, CubeData.Scale, 0.7f);

            float t = 0;
            while (t < 1) {
                t += Time.deltaTime * 3;
                transform.localScale = Vector3.Lerp(baseScale, endScale, t);

                yield return null;
            }

            t = 0;
            while (t < 1) {
                t += Time.deltaTime * 3;
                transform.localScale = Vector3.Lerp(endScale, endScale2, t);

                yield return null;
            }

            transform.localScale = endScale2;
        }

        private void SetDesign() {
            rendererR.material = CubeData.BaseMaterial;
            tmpText.text = CubeData.Value + "";
        }

        private void PlayPopEffect() {
            if (audioSource.isPlaying) return;
            if (rb.velocity.magnitude < 2) return;

            audioSource.clip = hitSound[Random.Range(0, hitSound.Length)];
            audioSource.volume = 0.6f;

            audioSource.Play();
        }

        private void PlayMergeEffect() {
            audioSource.clip = mergeSound[Random.Range(0, mergeSound.Length)];
            audioSource.volume = 1.0f;
            audioSource.Play();
        }

        private void OnMouseUpAsButton() {
            PowerupManager._instance.OnClickCube(this);
        }



        [ContextMenu("Set Cube Data 7")]
        private void InitWithCube7() {
            Init(DataCollection._instance.CubeDatas[7]);
        }

        [ContextMenu("Set Cube Data 8")]
        private void InitWithCube8() {
            Init(DataCollection._instance.CubeDatas[8]);
        }

        [ContextMenu("Set Cube Data 9")]
        private void InitWithCube9() {
            Init(DataCollection._instance.CubeDatas[9]);
        }

        [ContextMenu("Set Cube Data 10")]
        private void InitWithCube10() {
            Init(DataCollection._instance.CubeDatas[10]);
        }
    }
}